package im.route.api.endpoint;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import im.route.api.body.PaocModel;
import im.route.api.config.ChannelNoProvider;
import im.route.api.header.PAOCRequestHeader;
import im.route.api.header.PAOCResponseHeader;
import im.route.api.util.Base64Util;
import im.route.api.util.DateUtil;
import im.route.api.util.DesedeCryptSimpleHelper;
import im.route.api.util.JsonUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
//import org.apache.logging.log4j.util.Base64Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
//import org.springframework.retry.annotation.Backoff;
//import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
//import tech.kbtg.IntegrationModule.config.ChannelNoProvider;
//import tech.kbtg.IntegrationModule.constants.PAOCConstant;
//import tech.kbtg.IntegrationModule.constants.RetryConstant;
//import tech.kbtg.IntegrationModule.constants.SystemConstant;
//import tech.kbtg.IntegrationModule.models.body.PaocModel;
//import tech.kbtg.IntegrationModule.models.exception.DecryptException;
//import tech.kbtg.IntegrationModule.models.exception.EncryptException;
//import tech.kbtg.IntegrationModule.models.exception.PaocAckFailException;
//import tech.kbtg.IntegrationModule.models.exception.PaocException;
//import tech.kbtg.IntegrationModule.models.header.PAOCRequestHeader;
//import tech.kbtg.IntegrationModule.models.header.PAOCResponseHeader;
//import tech.kbtg.IntegrationModule.services.SystemConfigService;
//import tech.kbtg.IntegrationModule.util.*;

import javax.annotation.Resource;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;

@Service
public class PaocServiceClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(PaocServiceClient.class);
    //private final RestTemplate paocRestTemplateNoneKeepAlive;
    //private final String PAOC_PATH;
    private DesedeCryptSimpleHelper helper;
    //private final String reqTimeFormat;
    //private final String reqChannelNo;
    //private final String rspChannelNo;
    //private final ObjectMapper objMap;
    
    @Resource
    private ChannelNoProvider channelNoProvider;

    /*@Autowired
    public PaocServiceClient(@Qualifier("paocRestTemplateNoneKeepAlive") RestTemplate paocRestTemplateNoneKeepAlive, SystemConfigService systemConfigService, DesedeCryptSimpleHelper desedeCryptSimpleHelper, MappingService mappingService) {
        this.paocRestTemplateNoneKeepAlive = paocRestTemplateNoneKeepAlive;
        this.PAOC_PATH = systemConfigService.getConfigValue(SystemConstant.PAOC_ENDPOINT);
        this.helper = desedeCryptSimpleHelper;
        this.reqChannelNo = systemConfigService.getConfigValue(SystemConstant.PAOC_HEADER_REQ_CHANNEL_NO);
        this.rspChannelNo = systemConfigService.getConfigValue(SystemConstant.PAOC_HEADER_RSP_CHANNEL_NO);
        this.reqTimeFormat = systemConfigService.getConfigValue(SystemConstant.PAOC_HEADER_REQ_TIME_FORMAT);
        this.objMap = new ObjectMapper();
    }

    public PaocModel encodeRequest(String data, String apiId, String transNo, String reqTime, String reqChannelNo) {
        PAOCRequestHeader requestHeader = new PAOCRequestHeader(apiId);
        if (StringUtils.isBlank(reqChannelNo)) {
            requestHeader.setReqChannelNo(this.reqChannelNo);
        } else {
            requestHeader.setReqChannelNo(reqChannelNo);
        }
        
        requestHeader.setRspChannelNo(this.rspChannelNo);

        if (StringUtils.isBlank(transNo)) {
            requestHeader.setTransNo("1111111111111111");
        } else {
            requestHeader.setTransNo(transNo);
        }

        if (StringUtils.isBlank(reqTime)) {
            LocalDateTime now = LocalDateTime.now();
            requestHeader.setReqTime(DateUtil.convertToString(now, reqTimeFormat));
        }

        requestHeader.setSign(helper.md5(data, StandardCharsets.UTF_8));
        requestHeader.setInfoContent(helper.encrypt(data));
        String requestHeaderJson = JsonUtils.convertObjToJsonString(requestHeader);
        String contentData = Base64Util.encode(requestHeaderJson);
        return new PaocModel(contentData);
    }

    @Retryable(value = {ConnectException.class,InterruptedException.class,SocketTimeoutException.class,SocketException.class},
            maxAttemptsExpression = RetryConstant.MAX_ATTEMPTS,
            backoff = @Backoff(delayExpression = RetryConstant.DELAY))
    public PAOCResponseHeader sendPostRequest(Object data, PAOCRequestHeader paocRequestHeader) {
        
        boolean isError = false;
        PAOCResponseHeader responseHeader = new PAOCResponseHeader();
        PAOCRequestHeader requestHeader = new PAOCRequestHeader(paocRequestHeader.getApiId());
        LocalDateTime now = LocalDateTime.now();
        requestHeader.setReqTime(DateUtil.convertToString(now, reqTimeFormat));
        requestHeader.setReqChannelNo(ObjectUtils.defaultIfNull(paocRequestHeader.getReqChannelNo(), reqChannelNo));
        requestHeader.setRspChannelNo(ObjectUtils.defaultIfNull(paocRequestHeader.getRspChannelNo(), rspChannelNo));
        
        requestHeader.setTransNo(paocRequestHeader.getTransNo());

        try {
            String jsonString = objMap.writeValueAsString(data);
            requestHeader.setSign(helper.md5(jsonString, StandardCharsets.UTF_8));
            requestHeader.setInfoContent(helper.encrypt(jsonString));
            LOGGER.info("Generate PAOC request to send data to {}. Header: {}", paocRequestHeader.getApiId(), requestHeader);
            LOGGER.info("Generate PAOC request to send data to {}. Body: {}", paocRequestHeader.getApiId(), data);
            String requestHeaderJson = objMap.writeValueAsString(requestHeader);
            String contentData = Base64Util.encode(requestHeaderJson);
            PaocModel request = new PaocModel(contentData);

            RequestEntity<PaocModel> requestEntity = RequestEntity.post(new URI(PAOC_PATH)).contentType(MediaType.APPLICATION_JSON).body(request);
            ResponseEntity<PaocModel> responseEntity = paocRestTemplateNoneKeepAlive.postForEntity(PAOC_PATH, requestEntity, PaocModel.class);
            LOGGER.info("Receive response from paoc.");

            if (HttpStatus.OK.compareTo(responseEntity.getStatusCode()) == 0) {
                PaocModel responseBody = responseEntity.getBody();
                responseHeader = decryptResponse(responseBody.getContentData());
                LOGGER.debug("Response data from paoc: {}", responseHeader);

                if (!PAOCConstant.HEADER_RET_CODE_SUCCESS.equals(responseHeader.getRetCode())) {
                    throw new PaocAckFailException("PAOC ack fail code: " + responseHeader.getRetCode() + " Description: " + responseHeader.getRetMsg(), null);
                } else {
                    LOGGER.info("PAOC ack success.");
                }
            } else {
                throw new PaocException("PAOC HTTP Status " + responseEntity.getStatusCode().toString() + " does not accept.", Thread.currentThread().getStackTrace()[1].getMethodName());
            }
        } catch (JsonProcessingException e) {
            LOGGER.error("Can't parse object to json string.");
            isError = true;
        } catch (EncryptException | DecryptException | URISyntaxException | PaocException e) {
            LOGGER.error(e.getMessage(), e);
            isError = true;
        } finally {
            if (isError) {
                responseHeader.setRetCode(PAOCConstant.HEADER_RET_CODE_FAILURE);
            }
        }

        return responseHeader;
    }

    public PaocModel generateResponseModel(Object data, PAOCRequestHeader paocRequestHeader, boolean isError) {
        PaocModel paocModel;
        PAOCResponseHeader responseHeader = new PAOCResponseHeader(paocRequestHeader.getApiId(), paocRequestHeader.getTransNo(), paocRequestHeader.getReqChannelNo(), paocRequestHeader.getRspChannelNo());

        if (isError) {
            responseHeader.setRetCode(PAOCConstant.HEADER_RET_CODE_FAILURE);
            responseHeader.setRetMsg(PAOCConstant.HEADER_RET_MSG_FAILURE);
        } else {
            responseHeader.setRetCode(PAOCConstant.HEADER_RET_CODE_SUCCESS);
            responseHeader.setRetMsg(PAOCConstant.HEADER_RET_MSG_SUCCESS);
        }

        try {
            if (data == null) {
                responseHeader.setSign("");
                responseHeader.setInfoContent("");
            } else {
                String jsonString = objMap.writeValueAsString(data);
                responseHeader.setSign(helper.md5(jsonString, StandardCharsets.UTF_8));
                responseHeader.setInfoContent(helper.encrypt(jsonString));
                LOGGER.debug("Paoc Response data: {}", jsonString);
            }

            String responseHeaderJson = objMap.writeValueAsString(responseHeader);
            LOGGER.debug("PAOCResponseHeader is {}", responseHeaderJson);
            String contentData = Base64Util.encode(responseHeaderJson);
            paocModel = new PaocModel(contentData);
        } catch (JsonProcessingException e) {
            LOGGER.error("Can't parse object to json string.", e);
            return null;
        } catch (EncryptException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

        return paocModel;
    }*/

    public PAOCRequestHeader decryptRequest(String contentData) {
        String jsonString = Base64Util.decode(contentData);
        PAOCRequestHeader requestHeader = JsonUtils.convertJsonStringToObj(jsonString, PAOCRequestHeader.class);
        channelNoProvider.setChannelNo(requestHeader.getReqChannelNo());

        if (requestHeader != null) {
            requestHeader.setInfoContentDecrypted(helper.decrypt(requestHeader.getInfoContent()).replace(System.getProperty("line.separator"), ""));
            LOGGER.debug("Value of decrypt info content of paoc request header: {}", requestHeader.getInfoContentDecrypted());
        }

        return requestHeader;
    }

    /*public PAOCResponseHeader decryptResponse(String contentData) throws JsonProcessingException {
        String jsonString = Base64Util.decode(contentData);
        PAOCResponseHeader responseHeader = objMap.readValue(jsonString, PAOCResponseHeader.class);
        responseHeader.setInfoContentDecrypted(helper.decrypt(responseHeader.getInfoContent()));
        return responseHeader;
    }*/
}
